﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
using System.Web.Mvc;
using System.Web.Security;
using ExcellentCRM.DataService.Exts;
using ExcellentCRM.DataService.Models;
using ExcellentCRM.Site.Authorize;
using ExcellentCRM.Site.Models;
using ExcellentCRM.Site.Utilities;
using Webdiyer.WebControls.Mvc;

namespace ExcellentCRM.Site.Controllers
{
    public class AccountController : BaseController
    {
        [AllowAnonymous]
        public ActionResult Login(string returnUrl)
        {
            ViewBag.ReturnUrl = returnUrl;
            WebSecurity.Logout();
            return View();
        }

        [HttpPost]
        [AllowAnonymous]
        public ActionResult Login(LoginModel model, string returnUrl)
        {
            if (ModelState.IsValid && WebSecurity.Login(model.UserName, model.Password, model.RememberMe))
            {
                return RedirectToLocal(returnUrl);
            }

            // If we got this far, something failed, redisplay form
            ModelState.AddModelError("", "The user name or password provided is incorrect.");
            return View(model);
        }

        public ActionResult LogOff()
        {
            WebSecurity.Logout();

            return RedirectToAction("Login");
        }

        [AllowAnonymous]
        public ActionResult Register()
        {
            return View();
        }

        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public ActionResult Register(RegisterModel model)
        {
            if (ModelState.IsValid)
            {
                // Attempt to register the user
                try
                {
                    WebSecurity.CreateUserAndAccount(model.UserName, model.Password);
                    WebSecurity.Login(model.UserName, model.Password);
                    return RedirectToAction("Index", "Home");
                }
                catch (MembershipCreateUserException e)
                {
                    ModelState.AddModelError("", ErrorCodeToString(e.StatusCode));
                }
            }

            // If we got this far, something failed, redisplay form
            return View(model);
        }

        public ActionResult Manage()
        {
            return View();
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Manage(SearchAcctModel saModel)
        {
            if (ModelState.IsValid)
            {
                var acct = new Account
                    {
                        AcctName = saModel.AcctName,
                        AcctType = saModel.AcctType ?? AccountType.None
                    };
                int pageSize = PresetValues.PageSize;
                int totalCount;
                var searchAccts = ServiceFactory.AcctService.Search(acct, pageSize,
                                                                           saModel.PageIndex,
                                                                           out totalCount);
                var pageList = new PagedList<Account>(searchAccts, saModel.PageIndex, pageSize,
                                                      totalCount);
                saModel.SearchResult = pageList;
            }
            return View(saModel);
        }

        public ActionResult CreateOrUpdate(long? acctId = null)
        {
            var depts =
                ServiceFactory.DeptService.GetModels()
                              .Select(p => new SelectListItem
                                  {
                                      Text = p.DeptName,
                                      Value = p.Id.ToString(CultureInfo.InvariantCulture)
                                  });
            ViewBag.DeptList = depts;
            var acctModel = new AccountModel();
            if (acctId.HasValue && acctId.Value > 0)
            {
                var acct = ServiceFactory.AcctService.GetModel(acctId.Value);
                acctModel.MergeFrom(acct);
                acctModel.Staff = (acct.Staff != null && acct.Staff.Any())
                                      ? acct.Staff.FirstOrDefault()
                                      : null;
                acctModel.StuClient = (acct.StuClients != null && acct.StuClients.Any())
                                          ? acct.StuClients.FirstOrDefault()
                                          : null;
            }
            return PartialView(acctModel);
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult CreateOrUpdate(AccountModel acctModel)
        {
            AutoRemoveModelError(acctModel);
            if (ModelState.IsValid)
            {
                var acct = new Account();
                acct.MergeFrom(acctModel);
                var sr = ServiceFactory.AcctService.Save(acct, acctModel.Staff, acctModel.StuClient);
                return Json(new AjaxResult(sr.Success, sr.Message) {Expandos = acct});
            }
            
            return Json(new AjaxResult(false, ModelErrorStr));
        }

        [HttpPost]
        public ActionResult Delete(long acctId)
        {
            var sr = ServiceFactory.AcctService.Delete(acctId);
            return Json(new AjaxResult(sr.Success, sr.Message));
        }

        public ActionResult ChangePassword()
        {
            return PartialView("_ChangePasswordPartial");
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult ChangePassword(LocalPasswordModel passModel)
        {
            if (ModelState.IsValid)
            {
                var ajaxResult = new AjaxResult(false);
                if (passModel.OldPassword != CurrentUserAcct.AcctPass)
                    ajaxResult.Message = "旧密码不正确";
                CurrentUserAcct.AcctPass = passModel.NewPassword;
                var sr = ServiceFactory.AcctService.Save(CurrentUserAcct);
                if (sr.Success)
                    ajaxResult.Success = true;
                else
                    ajaxResult.Message = "修改密码失败";
                return Json(ajaxResult);
            }
            return Json(new AjaxResult(false, "保存密码失败"));
        }

        /// <summary>
        /// 移除ModelState中非必要的错误
        /// </summary>
        /// <param name="acctModel"></param>
        private void AutoRemoveModelError(AccountModel acctModel)
        {
            if (acctModel == null || acctModel.AcctType == AccountType.None) return;
            switch (acctModel.AcctType)
            {
                case AccountType.Staff:
                    {
                        var stuClientKeys = 
                            ModelState.Keys.Where(p => p.StartsWith("StuClient.")).ToArray();
                        foreach (string stuClientKey in stuClientKeys)
                        {
                            ModelState.Remove(stuClientKey);
                        }
                    }
                    break;
                case AccountType.Student:
                    {
                        var staffKeys = 
                            ModelState.Keys.Where(p => p.StartsWith("Staff.")).ToArray();
                        foreach (string staffKey in staffKeys)
                        {
                            ModelState.Remove(staffKey);
                        }
                    }
                    break;
                case AccountType.Admin:
                    {
                        var keyArr =
                            ModelState.Keys.Where(p => p.StartsWith("StuClient.") || p.StartsWith("Staff."))
                                      .ToArray();
                        foreach (string key in keyArr)
                        {
                            ModelState.Remove(key);
                        }
                    }
                    break;
            }
        }

        #region Helpers
        private ActionResult RedirectToLocal(string returnUrl)
        {
            if (Url.IsLocalUrl(returnUrl))
            {
                return Redirect(returnUrl);
            }
            else
            {
                return RedirectToAction("Index", "Home");
            }
        }

        public enum ManageMessageId
        {
            ChangePasswordSuccess,
            SetPasswordSuccess,
            RemoveLoginSuccess,
        }

        private static string ErrorCodeToString(MembershipCreateStatus createStatus)
        {
            // See http://go.microsoft.com/fwlink/?LinkID=177550 for
            // a full list of status codes.
            switch (createStatus)
            {
                case MembershipCreateStatus.DuplicateUserName:
                    return "User name already exists. Please enter a different user name.";

                case MembershipCreateStatus.DuplicateEmail:
                    return "A user name for that e-mail address already exists. Please enter a different e-mail address.";

                case MembershipCreateStatus.InvalidPassword:
                    return "The password provided is invalid. Please enter a valid password value.";

                case MembershipCreateStatus.InvalidEmail:
                    return "The e-mail address provided is invalid. Please check the value and try again.";

                case MembershipCreateStatus.InvalidAnswer:
                    return "The password retrieval answer provided is invalid. Please check the value and try again.";

                case MembershipCreateStatus.InvalidQuestion:
                    return "The password retrieval question provided is invalid. Please check the value and try again.";

                case MembershipCreateStatus.InvalidUserName:
                    return "The user name provided is invalid. Please check the value and try again.";

                case MembershipCreateStatus.ProviderError:
                    return "The authentication provider returned an error. Please verify your entry and try again. If the problem persists, please contact your system administrator.";

                case MembershipCreateStatus.UserRejected:
                    return "The user creation request has been canceled. Please verify your entry and try again. If the problem persists, please contact your system administrator.";

                default:
                    return "An unknown error occurred. Please verify your entry and try again. If the problem persists, please contact your system administrator.";
            }
        }
        #endregion

        [HttpPost]
        public ActionResult VerifyAcctExists(string acctName, AccountType acctType = AccountType.None, long? id = null)
        {
            bool isValid = !ServiceFactory.AcctService.AcctIsExists(acctName, acctType, id);
            return Json(isValid, JsonRequestBehavior.DenyGet);
        }
    }
}
